1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.google.common.hash;
21
22 import static com.google.common.base.Preconditions.checkArgument;
23
24 import java.io.Serializable;
25 import java.nio.ByteBuffer;
26
27 import javax.annotation.Nullable;
28
29
30
31
32
33
34
35
36 final class SipHashFunction extends AbstractStreamingHashFunction implements Serializable {
37
38
39 private final int c;
40
41 private final int d;
42
43 private final long k0;
44 private final long k1;
45
46
47
48
49
50
51
52 SipHashFunction(int c, int d, long k0, long k1) {
53 checkArgument(c > 0,
54 "The number of SipRound iterations (c=%s) during Compression must be positive.", c);
55 checkArgument(d > 0,
56 "The number of SipRound iterations (d=%s) during Finalization must be positive.", d);
57 this.c = c;
58 this.d = d;
59 this.k0 = k0;
60 this.k1 = k1;
61 }
62
63 @Override public int bits() {
64 return 64;
65 }
66
67 @Override public Hasher newHasher() {
68 return new SipHasher(c, d, k0, k1);
69 }
70
71
72
73 @Override public String toString() {
74 return "Hashing.sipHash" + c + "" + d + "(" + k0 + ", " + k1 + ")";
75 }
76
77 @Override
78 public boolean equals(@Nullable Object object) {
79 if (object instanceof SipHashFunction) {
80 SipHashFunction other = (SipHashFunction) object;
81 return (c == other.c)
82 && (d == other.d)
83 && (k0 == other.k0)
84 && (k1 == other.k1);
85 }
86 return false;
87 }
88
89 @Override
90 public int hashCode() {
91 return (int) (getClass().hashCode() ^ c ^ d ^ k0 ^ k1);
92 }
93
94 private static final class SipHasher extends AbstractStreamingHasher {
95 private static final int CHUNK_SIZE = 8;
96
97
98 private final int c;
99
100 private final int d;
101
102
103
104
105
106 private long v0 = 0x736f6d6570736575L;
107 private long v1 = 0x646f72616e646f6dL;
108 private long v2 = 0x6c7967656e657261L;
109 private long v3 = 0x7465646279746573L;
110
111
112 private long b = 0;
113
114
115
116 private long finalM = 0;
117
118 SipHasher(int c, int d, long k0, long k1) {
119 super(CHUNK_SIZE);
120 this.c = c;
121 this.d = d;
122 this.v0 ^= k0;
123 this.v1 ^= k1;
124 this.v2 ^= k0;
125 this.v3 ^= k1;
126 }
127
128 @Override protected void process(ByteBuffer buffer) {
129 b += CHUNK_SIZE;
130 processM(buffer.getLong());
131 }
132
133 @Override protected void processRemaining(ByteBuffer buffer) {
134 b += buffer.remaining();
135 for (int i = 0; buffer.hasRemaining(); i += 8) {
136 finalM ^= (buffer.get() & 0xFFL) << i;
137 }
138 }
139
140 @Override public HashCode makeHash() {
141
142 finalM ^= b << 56;
143 processM(finalM);
144
145
146 v2 ^= 0xFFL;
147 sipRound(d);
148 return HashCode.fromLong(v0 ^ v1 ^ v2 ^ v3);
149 }
150
151 private void processM(long m) {
152 v3 ^= m;
153 sipRound(c);
154 v0 ^= m;
155 }
156
157 private void sipRound(int iterations) {
158 for (int i = 0; i < iterations; i++) {
159 v0 += v1;
160 v2 += v3;
161 v1 = Long.rotateLeft(v1, 13);
162 v3 = Long.rotateLeft(v3, 16);
163 v1 ^= v0;
164 v3 ^= v2;
165 v0 = Long.rotateLeft(v0, 32);
166 v2 += v1;
167 v0 += v3;
168 v1 = Long.rotateLeft(v1, 17);
169 v3 = Long.rotateLeft(v3, 21);
170 v1 ^= v2;
171 v3 ^= v0;
172 v2 = Long.rotateLeft(v2, 32);
173 }
174 }
175 }
176
177 private static final long serialVersionUID = 0L;
178 }